home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Graphics / Misc / Wood.0.72 / Sources / WoodApp.m < prev    next >
Encoding:
Text File  |  1995-09-14  |  22.0 KB  |  878 lines

  1. #import <sys/dir.h>
  2. #import <misckit/MiscString.h>
  3. #import <misckit/MiscInfoController.h>
  4.  
  5. #import "wooddoc.h"
  6. #import "PageMargin.h"
  7. #import "WoodBundle.h"
  8. #import "ooe_server.h"
  9. #import "IPPanel.h"
  10. #import "IPFlyPanel.h"
  11.  
  12. #define    PAGE_LAYOUTSTR        NXLocalizedString("Page_Layout",NULL,Page_Layout)
  13. #define HELP_PANELSTR        NXLocalizedString("Help_Panel",NULL,Help_Panel)
  14. #define NOSTR                NXLocalizedString("NO",NULL,NO)
  15. #define YESSTR                NXLocalizedString("YES",NULL,YES)
  16. #define LIBRARYSUBPATH      "Wood"
  17. #define MODULEEXTENSION     "woodfilter"
  18.  
  19. static id docClass;                        // Class of Document to use
  20. static id zoneStorage;                    // zone storage for zone reuse
  21. static BOOL docClassConforms;            // YES if docClass conforms to protocol PDocument
  22. static BOOL inMsgPrint = NO;            // YES if message print (Listener)
  23. static const char *versionFormat;        // Version Format String
  24. static const int classVersion  = 110;    // Version multiplied by 100
  25.  
  26. static void initMenu(id menu);
  27. static id documentInWindow(id window);
  28. static id findDocument(const char *name);
  29. static id openFile(const char *directory, const char *name, BOOL display);
  30. static BOOL fileNameHasExtension(const char *f, const char *e);
  31.  
  32. @implementation WoodApp:Application
  33.  
  34. + initialize
  35. {
  36.     if (self == [WoodApp class]) {
  37.         docClass = [WoodDoc class];
  38.         docClassConforms = YES;  
  39.         versionFormat = NXLocalizedString("Version %.2f",NULL,Version Format);
  40.          zoneStorage = [[Storage allocFromZone:[self zone]] initCount:0 elementSize:sizeof(NXZone *)
  41.                  description:@encode(NXZone *)];
  42.     }
  43.     return self;
  44. }
  45.  
  46. - setDocClass:aDocClass
  47. {
  48.     docClass = aDocClass;
  49.     docClassConforms = YES;
  50.     return self;
  51. }
  52.  
  53. - docClass
  54. {
  55.     return docClass;
  56. }
  57.  
  58. - inspectorManager
  59. {
  60.     return inspectorManager;
  61. }
  62.  
  63. - (BOOL)docClassConforms
  64. {
  65.        return docClassConforms;
  66. }
  67.  
  68. - loadLocalNib:(const char *)nibName owner:aOwner
  69. {
  70.     id bundle;
  71.     char buf[MAXPATHLEN + 1],*temp;
  72.     NXZone *zone;
  73.  
  74.     bundle = [NXBundle bundleForClass:[aOwner class]];
  75.     if([bundle getPath:buf 
  76.         forResource:nibName 
  77.         ofType:"nib"]){
  78.                 zone = NXCreateChildZone([self zone], vm_page_size, vm_page_size, YES);
  79.                 NXNameZone(zone, nibName);
  80.                    if([self loadNibFile:buf 
  81.                     owner:aOwner 
  82.                     withNames:NO 
  83.                     fromZone:zone]){
  84.                             NXMergeZone(zone);
  85.                             return self;
  86.                 } else {
  87.                             NX_MALLOC(temp,char,strlen(nibName) + 51);
  88.                             sprintf(temp,"%s %s\n",localString("Could not load requested resource"),nibName); 
  89.                             Notify(localString("Resource Error"),temp);
  90.                             NX_FREE(temp);
  91.                             return nil;
  92.                 }
  93.     } else {
  94.         NX_MALLOC(temp,char,strlen(nibName) + 51);
  95.         sprintf(temp,"%s %s\n",localString("Could not find requested resource"),nibName); 
  96.         Notify(localString("Resource Error"),temp);
  97.         NX_FREE(temp);
  98.         return nil;
  99.     }
  100. }
  101.  
  102. - openFile:(const char *)path file:(const char *)type flag:(BOOL)flag
  103. {
  104.     return openFile(path,type,flag);
  105. }
  106.  
  107. - currentDocument
  108. {
  109.     return documentInWindow([self mainWindow]);
  110. }
  111.  
  112. - (const char *)currentDirectory
  113. {
  114.     const char *retval;
  115.     WoodDoc *currentDocument;
  116.    
  117.     currentDocument = [self currentDocument];
  118.     if([currentDocument directory])
  119.         retval = [currentDocument directory];
  120.     else 
  121.         retval = defaultDir;
  122.     return retval;
  123. }
  124.  
  125. - (const char *)docExtension
  126. {
  127.        if(docClassConforms)
  128.         return [docClass docExtension];
  129.     else
  130.         return NULL;
  131. }
  132.        
  133. - setDefaultDir:(const char *)dir
  134. {
  135.     sprintf(defaultDir,"%s",dir);
  136.     return self;
  137. }
  138.  
  139. - (BOOL)saveAll
  140. {
  141.     return saveAll;
  142. }
  143.  
  144. - setSaveAll:(BOOL)value
  145. {
  146.     saveAll = value;
  147.     return self;
  148. }
  149.  
  150. - (BOOL)dumpAll
  151. {
  152.     return dumpAll;
  153. }
  154.  
  155. - setDumpAll:(BOOL)value
  156. {
  157.     dumpAll = value;
  158.     return self;
  159. }
  160.  
  161. - saveAsPanel:sender
  162. {
  163.     id savepanel = [SavePanel new];
  164.  
  165.     [savepanel setAccessoryView:nil];
  166.     if(docClassConforms)
  167.            [savepanel setRequiredFileType:[docClass docExtension]];
  168.     return savepanel;
  169. }
  170.  
  171. - pageLayout:sender
  172. {
  173.     return pageMargin;
  174. }
  175.  
  176. - (NXZone *)newDocZone
  177. {
  178.     NXZone *dummy;
  179.  
  180.     if (![zoneStorage count]) {
  181.         return NXCreateZone(vm_page_size, vm_page_size, YES);
  182.     } else {
  183.         dummy = *(NXZone **)[zoneStorage elementAt:[zoneStorage count] - 1];
  184.         [zoneStorage removeLastElement];
  185.         return dummy;
  186.     }
  187. }
  188.  
  189. - reuseDocZone:(NXZone *)aZone
  190. {
  191.     NXZone *dummy;
  192.  
  193.     dummy = aZone;
  194.     [zoneStorage addElement:(void *)&dummy];
  195.     NXNameZone(aZone, "Unused");
  196.     return self;
  197. }
  198.  
  199. - new:sender
  200. {
  201.     [[docClass allocFromZone:[self newDocZone]] init];
  202.     return self;
  203. }
  204.  
  205. - open:sender
  206. {
  207.     const char *directory;
  208.     const char *const *files;
  209.       const char *const allowedType[2] = {[self docExtension], NULL};
  210.     OpenPanel *openpanel = [[OpenPanel new] allowMultipleFiles:YES];
  211.  
  212.     directory = [self currentDirectory];
  213.     if (directory && (*directory == '/')) 
  214.         [openpanel setDirectory:directory];
  215.     if ([openpanel runModalForTypes:allowedType]) {
  216.         files = [openpanel filenames];
  217.         directory = [openpanel directory];
  218.         while (files && *files) {
  219.             haveOpenedDocument = openFile(directory, *files, YES) || haveOpenedDocument;
  220.             files++;
  221.         }
  222.     }
  223.     return self;
  224. }
  225.  
  226. - saveAll:sender
  227. {
  228.     int count;
  229.  
  230.     count = [docList count];
  231.     if(docClassConforms)
  232.         while (count--) {
  233.             [[docList objectAt:count] save:self];
  234.         }
  235.     return self;
  236. }
  237.  
  238. - print:sender
  239. {
  240.     if(docClassConforms)
  241.         return [[self currentDocument] print:sender];
  242.     else
  243.         return nil;
  244. }
  245.  
  246. - samples:sender
  247. {
  248.     NXBundle *bundle;
  249.     char buf[2048];
  250.     
  251.     bundle = [NXBundle mainBundle];
  252.     strcpy(buf,[bundle directory]);
  253.     strcat(buf,"/Samples");
  254.     if(![[Application workspace] openFile:buf])
  255.         NXBeep();
  256.     return self;
  257. }
  258.  
  259. - (BOOL)menuItemUpdate:menuCell
  260. {
  261.     SEL action;
  262.     id responder, target;
  263.     BOOL retval = NO;
  264.  
  265.     target = [menuCell target];
  266.     if(!target){
  267.         action = [menuCell action];
  268.         responder = [NXApp calcTargetForAction:action];
  269.         if([responder respondsTo:@selector(validateCommand:)]){
  270.             retval = [responder validateCommand:menuCell];
  271.         } else { 
  272.             if(![menuCell isEnabled] && responder){
  273.                 [menuCell setEnabled:YES];
  274.                 retval = YES;
  275.             } else if([menuCell isEnabled] && !responder){
  276.                 [menuCell setEnabled:NO];
  277.                 retval = YES;
  278.             }
  279.         }
  280.     } else if(target == self) 
  281.             retval = [self validateCommand:menuCell];
  282.     return retval;
  283. }
  284.  
  285. - (BOOL)validateCommand:menuCell
  286. {
  287.     SEL action = [menuCell action];
  288.     BOOL redraw = NO, enabled = [menuCell isEnabled];
  289.     
  290.     if ((action == @selector(saveAll:)) || (action == @selector(print:))){
  291.          if(findDocument(NULL) && !enabled){
  292.             redraw = YES;
  293.             [menuCell setEnabled:YES];
  294.         } else if(!findDocument(NULL) && enabled){
  295.             redraw = YES;
  296.             [menuCell setEnabled:NO];
  297.         }
  298.     }
  299.     return redraw;
  300. }
  301.  
  302. - setupPageLayout:(float)lm :(float)rm :(float)tm :(float)bm 
  303. {
  304.     if (!pageMargin) {
  305.         pageMargin = [PageMargin new];
  306.         [pageMargin setPlpAccessory:plpAccessory];
  307.         [pageMargin setFrameUsingName:PAGE_LAYOUTSTR];
  308.     }
  309.     [pageMargin setValues:lm right:rm top:tm bottom:bm];
  310.     [pageMargin writePrintInfo];
  311.     return self;
  312. }
  313.  
  314. - appWillInit:sender
  315. {
  316.     char temp[MAXPATHLEN+1];
  317.     float lm,rm,tm,bm;
  318.     
  319.     if (!NXGetDefaultValue(appName,localString("SaveAll"))) 
  320.         sprintf(temp,NOSTR);
  321.     else 
  322.         sprintf(temp,NXGetDefaultValue(appName,localString("SaveAll")));
  323.     if (!strcmp(temp,YESSTR)) 
  324.         saveAll = YES;
  325.     if (!NXGetDefaultValue(appName,localString("DumpAll"))) 
  326.         sprintf(temp,NOSTR);
  327.     else 
  328.         sprintf(temp,NXGetDefaultValue(appName,localString("DumpAll")));
  329.     if (!strcmp(temp,YESSTR)) 
  330.         dumpAll = YES;
  331.     if (!NXGetDefaultValue(appName,localString("LeftMargin"))) 
  332.         sprintf(temp,"36.0");
  333.     else 
  334.         sprintf(temp,NXGetDefaultValue(appName,localString("LeftMargin")));
  335.     sscanf(temp,"%f",&lm);
  336.     if (!NXGetDefaultValue(appName,localString("RightMargin"))) 
  337.         sprintf(temp,"36.0");
  338.     else 
  339.         sprintf(temp,NXGetDefaultValue(appName,localString("RightMargin")));
  340.     sscanf(temp,"%f",&rm);
  341.     if (!NXGetDefaultValue(appName,localString("TopMargin"))) 
  342.         sprintf(temp,"36.0");
  343.     else 
  344.         sprintf(temp,NXGetDefaultValue(appName,localString("TopMargin")));
  345.     sscanf(temp,"%f",&tm);
  346.     if (!NXGetDefaultValue(appName,localString("BottomMargin"))) 
  347.         sprintf(temp,"36.0");
  348.     else 
  349.         sprintf(temp,NXGetDefaultValue(appName,localString("BottomMargin")));
  350.     sscanf(temp,"%f",&bm);
  351.     [self setupPageLayout:lm :rm :tm :bm];
  352.     [self setAutoupdate:YES];
  353.     docList = [[List alloc] init];
  354.     [self registerAsOOEServer];
  355.     return self;
  356. }
  357.  
  358. - appDidInit:sender
  359. {
  360.     int         i;
  361.     char         buffer[MAXPATHLEN+1], temp[MAXPATHLEN+1];
  362.     char         *directory, *name, *ext;
  363.     const char *def;
  364.     int ooeLaunched = NO;
  365.     id inspectorPanel;
  366.     
  367.     [self setDefaultDir:NXHomeDirectory()];
  368.     bundleList = [[List allocFromZone:[self zone]] init];
  369.       [self createBundlesAndLoadModules:NO];    
  370.     if (NXArgc > 1) {
  371.         for (i = 1; i < NXArgc; i++) {
  372.             strcpy(buffer, NXArgv[i]);
  373.             ext = rindex(buffer, '.');
  374.             if (!ext) {
  375.                 strcat(buffer,".");
  376.                 strcat(buffer, [self docExtension]);
  377.             }
  378.             if (*buffer == '/') {
  379.                 directory = "/";
  380.                 name = buffer;
  381.                 name++;
  382.             } else {
  383.                 name = rindex(buffer, '/');
  384.                 if (name) {
  385.                     *name++ = '\0';
  386.                     sprintf(temp,"%s/%s",[(NXBundle *)[NXBundle mainBundle] directory],buffer);
  387.                     directory = temp;
  388.                 } else {
  389.                     name = buffer;
  390.                     directory = NULL;
  391.                 }
  392.             }
  393.             haveOpenedDocument = openFile(directory, name, YES) || haveOpenedDocument;
  394.         }
  395.     }
  396.     def =  NXReadDefault([self appName], OOE_LAUNCH_DEFAULT);
  397.     ooeLaunched = (def != NULL && atoi(def) == 1);
  398.     NXWriteDefault([self appName], OOE_LAUNCH_DEFAULT, "0");
  399.     if (!haveOpenedDocument && !ooeLaunched && !NXGetDefaultValue([self appName], "NXServiceLaunch")) 
  400.         [self new:self];    
  401.     if (NXGetDefaultValue(appName,localString("Quit"))) {
  402.         [self activateSelf:YES];
  403.         NXPing();
  404.         [self terminate:self];
  405.     }
  406.     initMenu([self mainMenu]);
  407.     inspectorPanel = [inspectorManager window];
  408.     [inspectorPanel setFrameAutosaveName:"Wood Inspector Frame"];
  409.     [inspectorPanel setVisibleAutosaveName:"Wood Inspector Visibility"];
  410.     [inspectorPanel setFloatingAutosaveName:"Wood Inspector Floating"];
  411.     [inspectorPanel setFrameUsingName:[inspectorPanel frameAutosaveName]];
  412.     [inspectorPanel setFloatingUsingName:[inspectorPanel floatingAutosaveName]];
  413.     [inspectorPanel setVisibleUsingName:[inspectorPanel visibleAutosaveName]];
  414.     if([IPPanel isPanelSavedAsVisible:[inspectorPanel visibleAutosaveName]])
  415.         [inspectorManager inspect:nil];
  416.     [infoController setLicenseFile:[MiscString newWithString:"/Intro/License.rtfd"]];
  417.     [infoController setReleaseNotesFile:[MiscString newWithString:"/Intro/ReleaseNotes.rtfd"]];
  418.     return self;
  419. }
  420.  
  421. - app:sender willShowHelpPanel:panel
  422. {
  423.     char path[MAXPATHLEN + 1];
  424.  
  425.     [panel setFrameUsingName:HELP_PANELSTR];
  426.     sprintf(path, "%s/%s", [panel helpDirectory],"Intro/Intro.rtfd");
  427.     [panel showFile:path atMarker:NULL];
  428.     return self;
  429. }
  430.  
  431. - appWillTerminate:sender
  432. {
  433.     float lm,rm,tm,bm;
  434.     char temp[100];
  435.     id window, document;
  436.     int choice = 0, count = [docList count];
  437.     id inspectorPanel;
  438.  
  439.     inspectorPanel = [inspectorManager window];
  440.     [inspectorPanel saveFrameUsingName:[inspectorPanel frameAutosaveName]];
  441.     [inspectorPanel saveFloatingUsingName:[inspectorPanel floatingAutosaveName]];
  442.     [inspectorPanel saveVisibleUsingName:[inspectorPanel visibleAutosaveName]]; 
  443.     [pageMargin saveFrameUsingName:PAGE_LAYOUTSTR];
  444.     [pageMargin getValues:&lm right:&rm top:&tm bottom:&bm];
  445.     sprintf(temp,"%f",lm);
  446.     NXWriteDefault([self appName],localString("LeftMargin"),temp);
  447.     sprintf(temp,"%f",rm);
  448.     NXWriteDefault([self appName],localString("RightMargin"),temp);
  449.     sprintf(temp,"%f",tm);
  450.     NXWriteDefault([self appName],localString("TopMargin"),temp);
  451.     sprintf(temp,"%f",bm);
  452.     NXWriteDefault([self appName],localString("BottomMargin"),temp);
  453.     if (saveAll) {
  454.         while (count--) {
  455.             document = [docList objectAt:count];
  456.             if ([document respondsTo:@selector(needsSaving)] && [document needsSaving]) {
  457.                 [document save:self];
  458.             }
  459.         }
  460.         NXWriteDefault(appName,localString("SaveAll"),YESSTR);
  461.         NXWriteDefault(appName,localString("DumpAll"),NOSTR);
  462.     } else if (dumpAll) {
  463.         NXWriteDefault(appName,localString("DumpAll"),YESSTR);
  464.         NXWriteDefault(appName,localString("SaveAll"),NOSTR);
  465.     } else {
  466.         NXWriteDefault(appName,localString("DumpAll"),NOSTR);
  467.         NXWriteDefault(appName,localString("SaveAll"),NOSTR);
  468.         while (count--) {
  469.             document = [docList objectAt:count];
  470.             if ([document respondsTo:@selector(needsSaving)] && [document needsSaving]) {
  471.                 choice = NXRunAlertPanel(localString("Quit"),
  472.                                          localString("You have unsaved documents."),
  473.                                          localString("Review Unsaved"),
  474.                                          localString("Quit Anyway"),
  475.                                          localString("Cancel"));
  476.                 if (choice == NX_ALERTOTHER) 
  477.                     return nil;
  478.                 else if (choice == NX_ALERTALTERNATE) 
  479.                     return self;
  480.                 else if (choice == NX_ALERTDEFAULT) {
  481.                     count = 0;
  482.                     choice = [docList count];
  483.                     while (choice--) {
  484.                         document = [docList objectAt:choice];
  485.                         window = [document window];
  486.                         if ([document respondsTo:@selector(windowWillClose:)]) {
  487.                             if ([document windowWillClose:self]) 
  488.                                 [window close];
  489.                             else 
  490.                                 return nil;
  491.                         }
  492.                     }
  493.                 }
  494.             }
  495.         }
  496.     }
  497.     [[bundleList freeObjects] free];
  498.     return self;
  499. }
  500.  
  501. - (int)appOpenFile:(const char *)path type:(const char *)type
  502. {
  503.     char *name;
  504.     char directory[MAXPATHLEN+1];
  505.  
  506.     if (type && !strcmp(type, [self docExtension])) {
  507.         strcpy(directory, path);
  508.         name = rindex(directory, '/');
  509.         if (name) {
  510.             if (name != index(directory, '/')) {
  511.                 *name++ = '\0';
  512.                 if (openFile(directory, name, YES)) {
  513.                     haveOpenedDocument = YES;
  514.                     return YES;
  515.                 }
  516.             } else {
  517.                 name++;
  518.                 if (openFile("/", name, YES)) {
  519.                     haveOpenedDocument = YES;
  520.                     return YES;
  521.                 }
  522.             }
  523.         }
  524.     } else {
  525.         sprintf(directory,localString("%s is not a file %s can open.  An attempt to filter appropriate data will be made."),path,appName);
  526.         Notify(localString("File Open Warning"),directory);
  527.         if ([[docClass allocFromZone:[self newDocZone]] initFromPasteboard:[Pasteboard newByFilteringFile:path]]) 
  528.             return YES;
  529.     }
  530.     return NO;
  531. }
  532.  
  533. - (BOOL)appAcceptsAnotherFile:sender
  534. {
  535.     return YES;
  536. }
  537.  
  538. - (int)app:sender unmounting:(const char *)fullPath
  539. {
  540.     int    count        = [docList count];
  541.     WoodDoc    *document    = nil;
  542.     
  543.     if(!docClassConforms)
  544.         return 0;
  545.     while (count--) {
  546.         document = [docList objectAt:count];
  547.         if (!strncmp([document directory],fullPath,strlen(fullPath))) {
  548.             Notify(fullPath,localString("is being unmounted; documents here will close."));
  549.             [[document window] performClose:self];
  550.         }
  551.         
  552.     }
  553.     return 1;
  554. }        
  555.  
  556. - (int)msgDirectory:(const char **)fullPath ok:(int *)flag
  557. {
  558.     *fullPath = [self currentDirectory];
  559.     if (*fullPath) *flag = YES;
  560.     else {
  561.         *fullPath = "";
  562.         *flag = NO;
  563.     }
  564.     return 0;
  565. }
  566.  
  567. - (int)msgFile:(const char **)fullPath ok:(int *)flag
  568. {
  569.     const char *file;
  570.     
  571.     if(!docClassConforms)
  572.         return 0;
  573.     file = [(WoodDoc *)[self currentDocument] fileName];
  574.     if (file) {
  575.         *fullPath = file;
  576.         *flag = YES;
  577.     } else {
  578.         *fullPath = "";
  579.         *flag = NO;
  580.     }
  581.  
  582.     return 0;
  583. }
  584.  
  585. - (int)msgPrint:(const char *)fullPath ok:(int *)flag
  586. {
  587.     BOOL close;
  588.     id document = nil;
  589.     char *directory, *name;
  590.     char temp[MAXPATHLEN+1];
  591.     char path[MAXPATHLEN+1];
  592.     char buffer[MAXPATHLEN+1];
  593.  
  594.     inMsgPrint = YES;
  595.     strcpy(buffer, fullPath);
  596.     name = rindex(buffer, '/');
  597.     if (name) {
  598.         *name++ = '\0';
  599.         directory = buffer;
  600.     } else {
  601.         name = buffer;
  602.         getwd(temp);
  603.         directory = temp;
  604.     }
  605.     strcpy(path,directory);
  606.     strcat(path, "/");
  607.     strcat(path, name);
  608.     document = findDocument(path);
  609.     if (document) 
  610.         close = NO;
  611.     else {
  612.         document = openFile(directory, name, NO);
  613.         if (document) haveOpenedDocument = YES;
  614.         close = YES;
  615.     }
  616.     if (document && ![document isEmpty]) {
  617.         [self setPrintInfo:[document printInfo]];
  618.         if(docClassConforms)
  619.             [document print:self];
  620.         if (close) 
  621.             [[document window] performClose:self];
  622.         *flag = YES;
  623.     } else 
  624.         *flag = NO;
  625.     inMsgPrint = NO;
  626.     return 0;
  627. }
  628.  
  629. - (int)msgVersion:(const char **)aString ok:(int *)flag
  630. {
  631.     char buf[20];
  632.  
  633.     sprintf(buf, versionFormat, classVersion);
  634.     *aString = NXCopyStringBuffer(buf);
  635.     *flag = YES;
  636.     return 0;
  637. }
  638.  
  639. - (int)msgQuit:(int *)flag
  640. {
  641.     *flag = ([self terminate:self] ? NO : YES);
  642.     return 0;
  643. }
  644.  
  645. - registerDoc:aDoc
  646. {
  647.     [docList addObject:aDoc];
  648.     return self;
  649. }
  650.  
  651. - freeDoc:aDoc
  652. {
  653.     if([docList count] == 1)
  654.         [inspectorManager inspect:nil];
  655.     [docList removeObject:aDoc];
  656.     [aDoc free];
  657.     return self;
  658. }        
  659.  
  660. - docList
  661. {
  662.     return docList;
  663. }
  664.  
  665. - createBundlesAndLoadModules:(BOOL)doLoad
  666. {
  667.       char libraryDir[MAXPATHLEN + 1];
  668.     int i;
  669.     id cell, filter;
  670.   
  671.       [bundleList freeObjects];
  672.       strcpy(libraryDir, NXHomeDirectory());
  673.       strcat(libraryDir, "/Library/");
  674.       strcat(libraryDir, LIBRARYSUBPATH);
  675.       [self createBundlesForDirectory:libraryDir loadModules:doLoad];
  676.     strcpy(libraryDir, "/LocalLibrary/");
  677.       strcat(libraryDir, LIBRARYSUBPATH);
  678.       [self createBundlesForDirectory:libraryDir loadModules:doLoad];
  679.       strcpy(libraryDir, "/NextLibrary/");
  680.       strcat(libraryDir, LIBRARYSUBPATH);
  681.       [self createBundlesForDirectory:libraryDir loadModules:doLoad];
  682.       strcpy(libraryDir, [(NXBundle *)[NXBundle mainBundle] directory]);
  683.       [self createBundlesForDirectory:libraryDir loadModules:doLoad];
  684.     [addMenu disableFlushWindow];
  685.     for(i = 0; i < [bundleList count]; i++){
  686.         filter = [bundleList objectAt:i];
  687.         cell = [addMenu addItem:[filter filterName] action:@selector(addFromFilter:) keyEquivalent:0];
  688.         [cell setTag:i];
  689.         [cell setTarget:[self calcTargetForAction:@selector(addFromFilter:)]];
  690.     }
  691.     [addMenu reenableFlushWindow];
  692.     [addMenu flushWindow];   
  693.       return self;
  694. }
  695.  
  696. - loadFilter:sender
  697. {
  698.     const char *fileName;
  699.     id openpanel = [OpenPanel new], newWoodBundle, cell;
  700.     const char *const fileTypes[] = {MODULEEXTENSION, NULL};
  701.     BOOL shouldAdd;
  702.     int i, howMany;
  703.     char buf[MAXPATHLEN + 1], *tailOne, *tailTwo;
  704.  
  705.     [openpanel allowMultipleFiles:NO];
  706.     if([openpanel runModalForTypes:fileTypes]){
  707.         fileName = [openpanel filename];
  708.         shouldAdd = YES;
  709.         i = 0;
  710.         howMany = [bundleList count];
  711.         while(i < howMany && shouldAdd){
  712.             tailOne = rindex(fileName,'/') + 1;
  713.             tailTwo = rindex([(WoodBundle *)[bundleList objectAt:i] directory],'/') + 1;
  714.             shouldAdd = strcmp(tailOne, tailTwo) ? YES : NO;
  715.             i++;
  716.         }
  717.         if(shouldAdd){
  718.             newWoodBundle = [[WoodBundle allocFromZone:[self zone]]
  719.                           initForDirectory:fileName];
  720.             [bundleList addObject:newWoodBundle];
  721.             if([newWoodBundle getPath:buf forResource:"English" ofType:"lproj"])
  722.                 [[NXHelpPanel new] addSupplement:"Help" inPath:buf];
  723.             [addMenu disableFlushWindow];
  724.             cell = [addMenu addItem:[newWoodBundle filterName] 
  725.                 action:@selector(addFromFilter:) keyEquivalent:0];
  726.             [cell setTag:[bundleList count] - 1];
  727.             [cell setTarget:[self calcTargetForAction:@selector(addFromFilter:)]];
  728.             [addMenu reenableFlushWindow];
  729.             [addMenu flushWindow];
  730.         }            
  731.     }
  732.     return self;
  733. }
  734.  
  735. - createBundlesForDirectory:(const char *)dirPath loadModules:(BOOL)doLoad
  736. {
  737.       char modulePath[MAXPATHLEN + 1], buf[MAXPATHLEN + 1];
  738.       char *modulePathInsert, *tailOne, *tailTwo;
  739.       DIR *dir;
  740.       struct direct *dirEntry;
  741.       WoodBundle *newWoodBundle;
  742.     BOOL shouldAdd;
  743.     int i, howMany;
  744.  
  745.       strcpy(modulePath, dirPath);
  746.       modulePathInsert = modulePath + strlen(modulePath);
  747.       *modulePathInsert = '/';
  748.       modulePathInsert++;
  749.       dir = opendir(dirPath);
  750.       if(!dir)
  751.         return self;
  752.       while(dirEntry = readdir(dir)){
  753.         if((dirEntry->d_name[0] == '.')
  754.             || !fileNameHasExtension(dirEntry->d_name, MODULEEXTENSION))
  755.               continue;
  756.         strcpy(modulePathInsert, dirEntry->d_name);
  757.         shouldAdd = YES;
  758.         i = 0;
  759.         howMany = [bundleList count];
  760.         while(i < howMany && shouldAdd){
  761.             tailOne = modulePathInsert;
  762.             tailTwo = rindex([(WoodBundle *)[bundleList objectAt:i] directory],'/') + 1;
  763.             shouldAdd = strcmp(tailOne, tailTwo) ? YES : NO;
  764.             i++;
  765.         }
  766.         if(shouldAdd){
  767.             newWoodBundle = [[WoodBundle allocFromZone:[self zone]]
  768.                           initForDirectory:modulePath];
  769.             if([newWoodBundle getPath:buf forResource:"English" ofType:"lproj"])
  770.                 [[NXHelpPanel new] addSupplement:"Help" inPath:buf];
  771.             [bundleList addObject:newWoodBundle];
  772.             if(doLoad)
  773.                   [newWoodBundle filter];
  774.         }
  775.       }
  776.       closedir(dir);
  777.       return self;
  778. }
  779.  
  780. - filter:(int)nr
  781. {
  782.     return [[bundleList objectAt:nr] filter];
  783. }
  784.  
  785. @end
  786.  
  787. static void initMenu(id menu)
  788. {
  789.     int count;
  790.     id matrix, cell;
  791.     id matrixTarget, cellTarget;
  792.  
  793.     matrix = [menu itemList];
  794.     matrixTarget = [matrix target];
  795.     count = [matrix cellCount];
  796.     while (count--) {
  797.         cell = [matrix cellAt:count :0];
  798.         cellTarget = [cell target];
  799.         if (!matrixTarget && !cellTarget) {
  800.             [cell setUpdateAction:@selector(menuItemUpdate:) forMenu:menu];
  801.         } else if ([cell hasSubmenu]) {
  802.             initMenu(cellTarget);
  803.         } else if (cellTarget == [NXApp delegate])
  804.             [cell setUpdateAction:@selector(menuItemUpdate:) forMenu:menu];
  805.     }
  806. }
  807.  
  808. static id documentInWindow(id window)
  809. {
  810.     id document = [window delegate];
  811.  
  812.     if (document) 
  813.         return [document isKindOf:docClass] ? document : nil;
  814.     else 
  815.         return nil;
  816. }
  817.  
  818. static id findDocument(const char *name)
  819. {
  820.     int count;
  821.     WoodDoc *document; 
  822.     id docList;
  823.  
  824.     docList = [NXApp docList];
  825.     count = [docList count];
  826.     while (count--) {
  827.         document = [docList objectAt:count];
  828.         if (!name || !strcmp([document fileName], name)) 
  829.             return document;
  830.     }
  831.     return nil;
  832. }
  833.  
  834. static id openFile(const char *directory, const char *name, BOOL display)
  835. {
  836.     id window;
  837.     char buffer[MAXPATHLEN+1], path[MAXPATHLEN+1], temp[MAXPATHLEN+1];
  838.  
  839.     if (name && *name) {
  840.         if (!directory) 
  841.             directory = ".";
  842.         else if (*directory != '/') {
  843.             strcpy(buffer, "./");
  844.             strcat(buffer, directory);
  845.             directory = buffer;
  846.             getwd(temp);
  847.             if (!chdir(directory) && getwd(path)) 
  848.                 chdir(temp);
  849.             else {
  850.                 Notify(localString("Open:  path invalid"), directory);
  851.                 return nil;
  852.             }
  853.         } else 
  854.             strcpy(path,directory);
  855.         strcat(path, "/");
  856.         strcat(path, name);
  857.         window = [findDocument(path) window];
  858.         if (window) {
  859.             if (display) 
  860.                 [window makeKeyAndOrderFront:window];
  861.             return [window delegate];
  862.         } else 
  863.             return [[docClass allocFromZone:[NXApp newDocZone]] initFromFile:path];
  864.     }
  865.     return nil;
  866. }
  867.  
  868. static BOOL fileNameHasExtension(const char *f, const char *e)
  869. {
  870.   const char         *dot;
  871.  
  872.   if (f && e && (dot = strrchr(f, '.')) && !strcmp(dot + 1, e))
  873.     return YES;
  874.   else
  875.     return NO;
  876. }
  877.  
  878.